package com.zsw.service.impl;

import com.zsw.dao.OrderDao;
import com.zsw.entity.Commodity;
import com.zsw.entity.Order;
import com.zsw.exception.BusinessException;
import com.zsw.exception.UserErrorEnum;
import com.zsw.rpc.AccountFeignClient;
import com.zsw.rpc.CommodityFeignClient;
import com.zsw.rpc.StorageFeignClient;
import com.zsw.service.OrderService;
import com.zsw.vo.StorageVO;
import io.seata.spring.annotation.GlobalTransactional;
import io.seata.tm.api.GlobalTransaction;
import io.seata.tm.api.GlobalTransactionContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author zhangsw
 * <p>
 *     @Transactional(rollbackFor = {Exception.class})不能用于在GlobalTransactional的外层
 * </p>
 */
@Service
public class OrderServiceImpl implements OrderService {

    private final Lock lock = new ReentrantLock();

    @Resource
    private OrderDao orderDao;

    @Resource
    AccountFeignClient accountFeignClient;

    @Resource
    StorageFeignClient storageFeignClient;

    @Resource
    CommodityFeignClient commodityFeignClient;

    /**
     * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
     * <p>
     *   @GlobalTransactional 单独使用可能存在RPC TIME OUT
     * </p>
     */
    @Override
    public Order createOrder(int userId, int commodityCode, int orderCount) throws Exception {
//      手动控制Seata的分布式事务是为了解决远程调用超时的问题
        GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
        //获得当前事务进行的标记
        String xid = tx.getXid();

        lock.lock();
        try {
//            开始执行事务
            tx.begin(30000, xid);
            Commodity commodity = commodityFeignClient.getCommodityById(commodityCode);
            BigDecimal orderMoney = commodity.getPrice().multiply(BigDecimal.valueOf(orderCount));
            Order order = new Order() {{
                setUserId(userId);
                setCommodityCode(commodityCode);
                setCount(orderCount);
                setMoney(orderMoney);
                setStatus(3);
            }};
            int result = orderDao.insertOrder(order);
            if (result > 0) {
                accountFeignClient.deductAccountBalance(userId, orderMoney);
                storageFeignClient.deductStorage(new StorageVO(){{
                    setCommodityCode(commodityCode);
                    setCount(orderCount);
                }});
                Integer orderId = order.getId();
                //执行成功提交
                tx.commit();
                return orderDao.getOrderById(orderId);
            } else {
                //失败回滚
                tx.rollback();
                throw new BusinessException("创建订单失败");
            }
        } catch (Exception ex) {
            //异常回滚
            tx.rollback();
            throw new BusinessException(ex.getMessage());
        } finally {
            lock.unlock();
        }
    }
}
